package gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Stack;

import system.DrawingOperation;
import system.ImagePart;
import system.SystemProperties;

/**
 * @author Kevin Peters
 */
public class DrawingPanel extends GridPanel implements MouseListener,
		MouseMotionListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = -8306935131298926656L;

	BufferedImage image;
	private int scaling = SystemProperties.BASICSCALING;
	private int currentXPos;
	private int currentYPos;
	private Color drawingColor;
	private ImagePart imagePart;
	private boolean paintRectangle;
	private Stack<DrawingOperation> undoList = new Stack<DrawingOperation>();
	public static final int DRAW_RECTANGLE = 0;
	public static final int FILL_RECTANGLE = 1;
	public static final int PICK_COLOR = 2;
	private MainPanel mainPanel;
	private boolean fillRectangle;

	public ImagePart getImagePart() {
		return imagePart;
	}

	public void setImagePart(ImagePart imagePart) {
		this.imagePart = imagePart;
	}

	public Color getDrawingColor() {
		return drawingColor;
	}

	public void setDrawingColor(Color drawingColor) {
		this.drawingColor = drawingColor;
	}

	public DrawingPanel(MainPanel mainPanel, ImagePart imagePart,
			Stack<DrawingOperation> undoList) {
		super();
		this.mainPanel = mainPanel;
		this.imagePart = imagePart;
		this.image = imagePart.getImage();
		this.undoList = undoList;
		int baseImageWidth = image.getWidth();
		int baseImageHeight = image.getHeight();

		int scaledImageWidth = baseImageWidth * scaling;
		int scaledImageHeight = baseImageHeight * scaling;
		setPreferredSize(new Dimension(scaledImageWidth, scaledImageHeight));

		addMouseListener(this);
		addMouseMotionListener(this);
	}

	@Override
	protected void paintComponent(Graphics gr) {
		super.paintComponent(gr);
		Graphics2D g = (Graphics2D) gr;
		AffineTransform oldAT = g.getTransform();
		g.scale(scaling, scaling);

		g.drawImage(image, 0, 0, this);

		g.setTransform(oldAT);

		g.setColor(Color.RED);

		int snapX = ((int) (currentXPos / scaling)) * scaling;
		int snapY = ((int) (currentYPos / scaling)) * scaling;
		if (paintRectangle) {
			g.drawRect(snapX, snapY, scaling - 1, scaling - 1);
		}
	}

	@Override
	public void mouseMoved(MouseEvent e) {
		operate(e.getX(), e.getY(), DrawingPanel.DRAW_RECTANGLE);
	}

	@Override
	public void mouseDragged(MouseEvent e) {
		if (fillRectangle) {
			operate(e.getX(), e.getY(), DrawingPanel.FILL_RECTANGLE);
		}
	}

	@Override
	public void mousePressed(MouseEvent e) {
		if (e.getButton() == MouseEvent.BUTTON1) {
			operate(e.getX(), e.getY(), DrawingPanel.FILL_RECTANGLE);
			fillRectangle = true;
		} else {
			fillRectangle = false;
		}
	}

	/**
	 * Apply operation depending on mousebutton
	 * 
	 * @param currentXPos
	 * @param currentYPos
	 * @param operation
	 */
	public void operate(int currentXPos, int currentYPos, int operation) {
		this.currentXPos = currentXPos;
		this.currentYPos = currentYPos;
		int unscaledXpos = (int) (currentXPos / scaling);
		int unscaledYpos = (int) (currentYPos / scaling);

		switch (operation) {

		case DrawingPanel.DRAW_RECTANGLE: /*
										 * do nothing, just draw the cursor
										 * rectangle
										 */
			break;
		case DrawingPanel.FILL_RECTANGLE:
			setColorToPixel(unscaledXpos, unscaledYpos);
			break;
		case DrawingPanel.PICK_COLOR:
			getColorFromPixel(unscaledXpos, unscaledYpos);
			break;

		}
		repaint();
	}

	/**
	 * Receives the current color from the mouse position
	 */
	private void getColorFromPixel(int unscaledXpos, int unscaledYpos) {
		int currentColorValue = image.getRGB(unscaledXpos, unscaledYpos);
		Color currentColor = new Color(currentColorValue);
		mainPanel.getMaskGrid().noticeAllPanels(currentColor);
		mainPanel.getColorChooser().setColor(currentColor);
	}

	/**
	 * Computes the current mouse position an fills the square
	 */
	private void setColorToPixel(int unscaledXpos, int unscaledYpos) {

		try {
			Color colorAfter = getDrawingColor();
			int colorValueBefore = image.getRGB(unscaledXpos, unscaledYpos);
			int colorValueAfter = colorAfter.getRGB();
			image.setRGB(unscaledXpos, unscaledYpos, colorValueAfter);
			DrawingOperation drawingOperation = new DrawingOperation(this,
					unscaledXpos, unscaledYpos, colorValueBefore,
					colorValueAfter);
			if (!undoList.empty()) {
				DrawingOperation drawingOperationBefore = undoList.peek();
				if (!drawingOperationBefore.equals(drawingOperation)) {
					undoList.push(drawingOperation);
				}
			} else {
				undoList.push(drawingOperation);
			}

		} catch (Throwable t) {
			/* swallow it */
		}
	}

	@Override
	public void mouseClicked(MouseEvent e) {

		if (e.getButton() == MouseEvent.BUTTON3) {
			operate(e.getX(), e.getY(), DrawingPanel.PICK_COLOR);
		} else if (e.getButton() == MouseEvent.BUTTON1) {
			operate(e.getX(), e.getY(), DrawingPanel.FILL_RECTANGLE);
		}

	}

	@Override
	public void mouseEntered(MouseEvent e) {
		paintRectangle = true;

	}

	@Override
	public void mouseExited(MouseEvent e) {
		paintRectangle = false;
		repaint();
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub
	}

	@Override
	public int getId() {
		return imagePart.getEditorIndex();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + currentXPos;
		result = prime * result + currentYPos;
		result = prime * result
				+ ((drawingColor == null) ? 0 : drawingColor.hashCode());
		result = prime * result + ((image == null) ? 0 : image.hashCode());
		result = prime * result
				+ ((imagePart == null) ? 0 : imagePart.hashCode());
		result = prime * result + (paintRectangle ? 1231 : 1237);
		result = prime * result + scaling;
		result = prime * result
				+ ((undoList == null) ? 0 : undoList.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		DrawingPanel other = (DrawingPanel) obj;
		if (currentXPos != other.currentXPos)
			return false;
		if (currentYPos != other.currentYPos)
			return false;
		if (drawingColor == null) {
			if (other.drawingColor != null)
				return false;
		} else if (!drawingColor.equals(other.drawingColor))
			return false;
		if (image == null) {
			if (other.image != null)
				return false;
		} else if (!image.equals(other.image))
			return false;
		if (imagePart == null) {
			if (other.imagePart != null)
				return false;
		} else if (!imagePart.equals(other.imagePart))
			return false;
		if (paintRectangle != other.paintRectangle)
			return false;
		if (scaling != other.scaling)
			return false;
		if (undoList == null) {
			if (other.undoList != null)
				return false;
		} else if (!undoList.equals(other.undoList))
			return false;
		return true;
	}

}